package com.omni.monitor.plugin.utils;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;

public class ReflectionUtils {


    /**
     * 根据包名获取包下面所有的类名
     *
     * @param pack 包名称
     * @return ss
     */
    public static Set<Class<?>> getClasses(String pack) {

        // 第一个class类的集合
        Set<Class<?>> classes = new LinkedHashSet<>();
        // 是否循环迭代
        boolean recursive = true;
        // 获取包的名字 并进行替换
        String packageDirName = pack.replace('.', '/');
        // 定义一个枚举的集合 并进行循环来处理这个目录下的things
        Enumeration<URL> dirs;
        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            // 循环迭代下去
            while (dirs.hasMoreElements()) {
                // 获取下一个元素
                URL url = dirs.nextElement();
                // 得到协议的名称
                String protocol = url.getProtocol();
                // 如果是以文件的形式保存在服务器上
                if ("file".equals(protocol)) {
                    // 获取包的物理路径
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    // 以文件的方式扫描整个包下的文件 并添加到集合中
                    findClassesInPackageByFile(pack, filePath, true, classes);
                } else if ("jar".equals(protocol)) {
                    // 如果是jar包文件
                    // 定义一个JarFile
                    System.out.println("jar类型的扫描");
                    JarFile jar;
                    try {
                        // 获取jar
                        jar = ((JarURLConnection) url.openConnection()).getJarFile();
                        // 从此jar包 得到一个枚举类
                        Enumeration<JarEntry> entries = jar.entries();
                        findClassesInPackageByJar(pack, entries, packageDirName, true, classes);
                    } catch (IOException e) {
                        // log.error("在扫描用户定义视图时从jar包获取文件出错");
                        e.printStackTrace();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classes;
    }

    /**
     * 以文件的形式来获取包下的所有Class
     *
     * @param packageName 包名称
     * @param packagePath 路径
     * @param recursive 过滤
     * @param classes 类
     */
    private static void findClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<Class<?>> classes) {
        // 获取此包的目录 建立一个File
        File dir = new File(packagePath);
        // 如果不存在或者 也不是目录就直接返回
        if (!dir.exists() || !dir.isDirectory()) {
            // log.warn("用户定义包名 " + packageName + " 下没有任何文件");
            return;
        }
        // 如果存在 就获取包下的所有文件 包括目录
        File[] dirfiles = dir.listFiles(new FileFilter() {
            // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
            @Override
            public boolean accept(File file) {
                return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
            }
        });
        // 循环所有文件
        if (dirfiles==null)
        {
            return;
        }
        for (File file : dirfiles) {
            // 如果是目录 则继续扫描
            if (file.isDirectory()) {
                findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
            } else {
                // 如果是java类文件 去掉后面的.class 只留下类名
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    // 添加到集合中去
                    // classes.add(Class.forName(packageName + '.' +
                    // className));
                    // 经过回复同学的提醒，这里用forName有一些不好，会触发static方法，没有使用classLoader的load干净
                    classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
                } catch (ClassNotFoundException e) {
                    // log.error("添加用户自定义视图类错误 找不到此类的.class文件");
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 以jar的形式来获取包下的所有Class
     *
     * @param packageName 包名
     * @param entries  集合
     * @param packageDirName 文件夹名
     * @param recursive 是否循环迭代
     * @param classes 类
     */
    private static void findClassesInPackageByJar(String packageName, Enumeration<JarEntry> entries, String packageDirName, final boolean recursive, Set<Class<?>> classes) {
        // 同样的进行循环迭代
        while (entries.hasMoreElements()) {
            // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
            JarEntry entry = entries.nextElement();
            String name = entry.getName();
            // 如果是以/开头的
            if (name.charAt(0) == '/') {
                // 获取后面的字符串
                name = name.substring(1);
            }
            // 如果前半部分和定义的包名相同
            if (name.startsWith(packageDirName)) {
                int idx = name.lastIndexOf('/');
                // 如果以"/"结尾 是一个包
                if (idx != -1) {
                    // 获取包名 把"/"替换成"."
                    packageName = name.substring(0, idx).replace('/', '.');
                }
                // 如果可以迭代下去 并且是一个包
                if ((idx != -1) || recursive) {
                    // 如果是一个.class文件 而且不是目录
                    if (name.endsWith(".class") && !entry.isDirectory()) {
                        // 去掉后面的".class" 获取真正的类名
                        String className = name.substring(packageName.length() + 1, name.length() - 6);
                        try {
                            // 添加到classes
                            classes.add(Class.forName(packageName + '.' + className));
                        } catch (ClassNotFoundException e) {
                            // .error("添加用户自定义视图类错误 找不到此类的.class文件");
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }


    /**
     * 根据类信息获取对应的源代码文件路径
     * 需配置源码根目录（如monitorConfig.getSourceRoot() = "src/main/java"）
     */
    public static String getSourceFilePath(Class<?> clazz,String sourceRoot) {
        try {
            String packageName = clazz.getPackage().getName();
            String className = clazz.getSimpleName() + ".java";
            // 包名转换为路径（com.example.controller -> com/example/controller）
            String packagePath = packageName.replace(".", File.separator);
            // 拼接源码根路径（如src/main/java/com/example/controller/XXX.java）
            // 需要在配置中添加源码根路径
            String trimmedPath= Paths.get(sourceRoot, packagePath, className).toString();
            String projectRoot = System.getProperty("user.dir");
            // 拼接完整路径（去掉开头的"/"，避免多一层目录）
            return projectRoot + trimmedPath;
        } catch (Exception e) {
            // 日志记录：无法获取源文件路径，e.getMessage()
            return null;
        }
    }

    /**
     * 读取源代码文件内容（按行）
     */
    /**
     * 读取指定路径的源代码文件（.java/.txt等文本文件）
     * @param filePath 文件路径（支持绝对路径或相对路径）
     * @return 文件内容按行存储的列表，读取失败返回空列表（非null）
     */
    public static List<String> readSourceFile(String filePath) {
        // 1. 校验路径非空
        if (filePath == null || filePath.trim().isEmpty()) {
            return Collections.emptyList();
        }

        Path file = Paths.get(filePath.trim());

        // 2. 校验文件是否存在
        if (!Files.exists(file)) {
            return Collections.emptyList();
        }

        // 3. 校验是否为文件（非目录）
        if (!Files.isRegularFile(file)) {
            return Collections.emptyList();
        }

        // 4. 读取文件内容
        try {
            List<String> content = Files.readAllLines(file, StandardCharsets.UTF_8);
            return content;
        } catch (Exception e) {
            // 捕获所有读取相关异常（IO异常、权限异常等）
            return Collections.emptyList();
        }
    }

    /**
     * 解析方法的Javadoc注释
     * 逻辑：找到方法定义的起始行，向上查找/** ... * /格式的注释
     */
    public static String parseMethodJavadoc(List<String> sourceLines, Method method) {
        // 1. 构建方法签名的特征字符串（用于匹配方法定义行）
        // 例如：public String getUserById(Long id) -> 提取"getUserById"和参数类型
        String methodName = method.getName();
        Class<?>[] paramTypes = method.getParameterTypes();
        String paramTypeStr = Arrays.stream(paramTypes)
                .map(Class::getSimpleName)
                .collect(Collectors.joining(","));
        String methodSignatureFeature = methodName + "\\(.*" + paramTypeStr + ".*\\)"; // 简化的特征匹配

        // 2. 找到方法定义的起始行号
        int methodStartLine = -1;
        for (int i = 0; i < sourceLines.size(); i++) {
            String line = sourceLines.get(i).trim();
            if (line.matches(".*" + methodSignatureFeature + ".*\\{?")) { // 匹配方法定义行（可能带{）
                methodStartLine = i;
                break;
            }
        }
        if (methodStartLine == -1) {
            return null; // 未找到方法定义
        }

        // 3. 从方法定义行向上查找Javadoc（/**开头，*/结尾）
        StringBuilder javadoc = new StringBuilder();
        int lineNum = methodStartLine - 1;
        boolean inJavadoc = false;

        while (lineNum >= 0) {
            String line = sourceLines.get(lineNum).trim();
            if (line.startsWith("*/")) {
                // 结束标记，停止查找
                inJavadoc = false;
                break;
            }
            if (inJavadoc) {
                // 拼接注释内容（过滤掉*号和空格）
                javadoc.insert(0, line.replaceAll("^\\*+\\s?", "").trim() + "\n");
            }
            if (line.startsWith("/**")) {
                // 开始标记，进入Javadoc区域
                inJavadoc = true;
                // 处理当前行的内容（可能包含/**开头的部分）
                javadoc.insert(0, line.replace("/**", "").trim() + "\n");
            }
            lineNum--;
        }

        // 4. 清理注释内容（去除空行、多余换行）
        String result = javadoc.toString().trim().replaceAll("\n+", "\n");
        return result.isEmpty() ? null : result;
    }
}
